package com.example.demo.utils.wx;

import com.example.demo.utils.common.StringUtils;
import com.example.demo.utils.date.DateUtils;
import com.example.demo.utils.http.ServletUtils;
import com.example.demo.utils.returned.R;
import lombok.extern.slf4j.Slf4j;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Map;
import java.util.SortedMap;
import java.util.TreeMap;

/**
 * 微信支付API
 */
@Slf4j
public class WxPayService {

    /**
     * 统一下单
     * 应用场景：商户系统先调用该接口在微信支付服务后台生成预支付交易单，返回正确的预支付交易回话标识后再在APP里面调起支付。
     */
    public R<WxPayEntity> WXPrePay(WxPayEntity wxPayEntity) {
        /** 根据支付类型，进行APPID的设置*/
        String appId = StringUtils.equals(wxPayEntity.getTradeType(), WxConfig.FROM_APP) ? WxConfig.appid : WxConfig.publicappid;
        /** 生成随机串 */
        String nonceStr = WxUtil.getRandomString(32);
        /** 订单金额-分为单位 */
        BigDecimal totalFee = new BigDecimal(wxPayEntity.getTotalFee()).multiply(new BigDecimal(100)).setScale(0, RoundingMode.HALF_UP);
        /** 准备下单数据-首次签名 */
        SortedMap<String, Object> signMap1 = new TreeMap<String, Object>();
        signMap1.put("appid", appId);
        signMap1.put("mch_id", WxConfig.merid);
        signMap1.put("nonce_str", nonceStr);
        signMap1.put("body", wxPayEntity.getBody()); // 商品信息
        signMap1.put("out_trade_no", wxPayEntity.getOutTradeNo());
        signMap1.put("total_fee", totalFee.intValue());
        signMap1.put("fee_type", "CNY");
        signMap1.put("spbill_create_ip", WxUtil.getRemoteHost(ServletUtils.getRequest()));
        signMap1.put("notify_url", WxConfig.notify);
        signMap1.put("trade_type", wxPayEntity.getTradeType());
        /** 如果是微信内部调用，使用JSAPI调起，传入相关参数 */
        if (StringUtils.equals(wxPayEntity.getTradeType(), WxConfig.FROM_JSAPI)) {
            signMap1.put("openid", wxPayEntity.getOpenId());
            signMap1.put("sign_type", "MD5");
        }
        /** 设置订单过期时间，默认半小时 */
        signMap1.put("time_start", DateUtils.format(DateUtils.now(), DateUtils._yyyyMMddHHmmss));
        signMap1.put("time_expire", DateUtils.format(DateUtils.addMinutes(DateUtils.now(), 30), DateUtils._yyyyMMddHHmmss));
        /** 第一次签名 */
        String sign1 = WxUtil.createSign("UTF-8", signMap1, WxConfig.merkey, true);
        signMap1.put("sign", sign1);
        String requestXML = WxUtil.getRequestXml(signMap1);
        System.out.println("微信统一下单xml格式的请求参数:" + requestXML);
        String result = WxUtil.sslRequest(
                WxConfig.unifiedorderUrl, requestXML,
                ServletUtils.getRequest(), WxConfig.merid, WxConfig.merCert);
        System.out.println("微信统一下单响应结果:" + result);
        try {

            Map<String, String> map = WxUtil.doXMLParse(result);
            /** 获取时间戳 */
            String timeStamp = WxUtil.getTimestamp();
            /** 二次签名 */
            SortedMap<String, Object> signMap2 = new TreeMap<String, Object>();
            signMap2.put("appid", appId);
            signMap2.put("timestamp", timeStamp);
            signMap2.put("noncestr", nonceStr);
            /** APP调用 */
            if (StringUtils.equals(wxPayEntity.getTradeType(), WxConfig.FROM_APP)) {
                signMap2.put("prepayid", map.get("prepay_id"));
                signMap2.put("package", "Sign=WXPay");
                signMap2.put("partnerid", WxConfig.merid);
            } else {
                /** JSAPI调用 */
                signMap2.put("package", "prepay_id=" + map.get("prepay_id"));
                signMap2.put("signType", "MD5");
            }

            String sign2 = WxUtil.createSign("UTF-8", signMap2, WxConfig.merkey, true);
            /** 设置返回数据 */
            wxPayEntity.setPartnerid(WxConfig.merid);
            wxPayEntity.setPrepayid(map.get("prepay_id"));
            wxPayEntity.setMweb_url(map.get("mweb_url"));
            wxPayEntity.setNonceStr(nonceStr);
            wxPayEntity.setTimestamp(timeStamp);
            wxPayEntity.setPackAge(signMap2.get("package").toString());
            wxPayEntity.setSign(sign2);
            wxPayEntity.setExtData("购买类订单");
        } catch (Exception e) {
            log.error("微信统一下单：" + e.getMessage());
        }

        return R.data(wxPayEntity);
    }

    /**
     * 订单查询
     */
    public R<WxPayEntity> WXOrderQuery(WxPayEntity wxPayEntity) {
        /** 随机串 */
        String nonceStr = WxUtil.getRandomString(32);
        /** 根据支付类型，进行APPID的设置*/
        String appId = StringUtils.equals(wxPayEntity.getTradeType(), WxConfig.FROM_APP) ? WxConfig.appid : WxConfig.publicappid;
        /** 准备查询订单数据 */
        SortedMap<String, Object> signMap = new TreeMap<String, Object>();
        signMap.put("appid", appId);
        signMap.put("mch_id", WxConfig.merid);
        signMap.put("transaction_id", wxPayEntity.getTransactionId());
        signMap.put("nonce_str", nonceStr);
        String sign = WxUtil.createSign("UTF-8", signMap, WxConfig.merkey, true);
        signMap.put("sign", sign);
        /** 生成xml格式的请求参数 */
        String requestXML = WxUtil.getRequestXml(signMap);
        System.out.println("微信订单查询xml格式的请求参数:" + requestXML);
        String result = WxUtil.sslRequest(
                WxConfig.orderqueryUrl, requestXML,
                ServletUtils.getRequest(), WxConfig.merid, WxConfig.merCert);
        System.out.println("微信订单查询响应结果:" + result);
        Map<String, String> map = null;
        try {
            map = WxUtil.doXMLParse(result);

            /**
             * SUCCESS—支付成功
             * REFUND—转入退款
             * NOTPAY—未支付
             * CLOSED—已关闭
             * REVOKED—已撤销（刷卡支付）
             * USERPAYING--用户支付中
             * PAYERROR--支付失败(其他原因，如银行返回失败)
             */
            String status = map.get("trade_state");
            /** 支付中不处理 */
            if (StringUtils.equals("USERPAYING", status)) {
                return null;
            }

            /** 根据支付状态，做相关业务处理 */

        } catch (Exception e) {
            log.error("订单查询异常：" + e.getMessage());
        }
        return R.data(wxPayEntity);
    }

    /**
     * 退款
     */
    public R<WxPayEntity> WXPreRefund(WxPayEntity wxPayEntity) {
        /** 随机串 */
        String nonceStr = WxUtil.getRandomString(32);
        /** 根据支付类型，进行APPID的设置*/
        String appId = StringUtils.equals(wxPayEntity.getTradeType(), WxConfig.FROM_APP) ? WxConfig.appid : WxConfig.publicappid;
        /** 订单金额-分为单位 */
        BigDecimal totalFee = new BigDecimal(wxPayEntity.getTotalFee()).multiply(new BigDecimal(100)).setScale(0, RoundingMode.HALF_UP);
        /** 退款金额-分为单位 */
        BigDecimal refundFee = new BigDecimal(wxPayEntity.getRefundFee()).multiply(new BigDecimal(100)).setScale(0, RoundingMode.HALF_UP);
        /** 准备退款数据 */
        SortedMap<String, Object> signMap = new TreeMap<String, Object>();
        signMap.put("appid", appId);
        signMap.put("mch_id", WxConfig.merid);
        signMap.put("nonce_str", nonceStr);
        signMap.put("transaction_id", wxPayEntity.getTransactionId());
//        signMap.put("out_trade_no", wxEntity.getOutTradeNo());
        /** 一笔订单可分多次退款，一个单号，多个退款单号 */
        signMap.put("out_refund_no", wxPayEntity.getOutRefundNo());
        signMap.put("total_fee", totalFee.intValue());
        signMap.put("refund_fee", refundFee.intValue());
        signMap.put("notify_url", WxConfig.refundNotify);
        String sign = WxUtil.createSign("UTF-8", signMap, WxConfig.merkey, true);
        signMap.put("sign", sign);
        /** 生成xml格式的请求参数 */
        String requestXML = WxUtil.getRequestXml(signMap);
        System.out.println("微信订单退款xml格式的请求参数:" + requestXML);
        String result = WxUtil.sslRequest(
                WxConfig.refundUrl, requestXML,
                ServletUtils.getRequest(), WxConfig.merid, WxConfig.merCert);
        System.out.println("微信订单退款响应信息:" + result);

        try {
            Map<String, String> map = WxUtil.doXMLParse(result);
            String return_code = map.get("return_code");
            if (StringUtils.equals("SUCCESS", return_code)) {
                String result_code = map.get("result_code");
                wxPayEntity.setReturn_code(result_code);
                if (StringUtils.equals("FAIL", result_code)) {
                    String err_code_des = map.get("err_code_des");
                    wxPayEntity.setReturn_msg(err_code_des);
                }
            } else {
                String return_msg = map.get("return_msg");
                wxPayEntity.setReturn_code(return_code);
                wxPayEntity.setReturn_msg(return_msg);
            }
        } catch (Exception e) {
            log.error("退款异常：" + e.getMessage());
        }

        return R.data(wxPayEntity);
    }

    /**
     * 退款查询
     */
    public R<WxPayEntity> WXRefundQuery(WxPayEntity wxPayEntity) {

        /** 随机串 */
        String nonceStr = WxUtil.getRandomString(32);
        /** 根据支付类型，进行APPID的设置*/
        String appId = StringUtils.equals(wxPayEntity.getTradeType(), WxConfig.FROM_APP) ? WxConfig.appid : WxConfig.publicappid;
        /** 准备退款查询数据 */
        SortedMap<String, Object> signMap = new TreeMap<String, Object>();
        signMap.put("appid", appId);
        signMap.put("mch_id", WxConfig.merid);
        signMap.put("nonce_str", nonceStr);
//        signMap.put("transaction_id", wxEntity.getTransactionId());
        signMap.put("out_refund_no", wxPayEntity.getOutRefundNo());
        String sign = WxUtil.createSign("UTF-8", signMap, WxConfig.merkey, true);
        signMap.put("sign", sign);

        /** 生成xml格式的请求参数 */
        String requestXML = WxUtil.getRequestXml(signMap);
        System.out.println("微信订单退款查询xml格式的请求参数:" + requestXML);
        String result = WxUtil.sslRequest(
                WxConfig.refundqueryUrl, requestXML,
                ServletUtils.getRequest(), WxConfig.merid, WxConfig.merCert);
        System.out.println("微信订单退款查询响应信息:" + result);

        try {
            Map<String, String> map = WxUtil.doXMLParse(result);

            /**
             * SUCCESS—退款成功
             * REFUNDCLOSE—退款关闭。
             * PROCESSING—退款处理中
             * CHANGE—退款异常
             */
            String refundStatus = map.get("refund_status");
            if (StringUtils.equals("PROCESSING", refundStatus)) {
                return null;
            }

            /** 根据退款状态，处理相关业务 */

        } catch (Exception e) {
            log.error("退款查询异常：" + e.getMessage());
        }

        return R.data(wxPayEntity);
    }

}
